"""
恢复命令实现

实现restore命令的具体功能。
"""

import click
from pathlib import Path
from typing import Dict, Any, Optional

from .base import BaseCommand
from ...core.restore import RestoreManager, RestoreStrategy
from ...core.metadata import MetadataManager
from ...utils.exceptions import ESArchiveError


class RestoreCommand(BaseCommand):
    """恢复命令实现"""
    
    def __init__(self, config, debug_mode: bool = False):
        """初始化恢复命令"""
        super().__init__(config, debug_mode=debug_mode)
        restore_config = config.get('restore', {})
        self.restore_manager = RestoreManager(self.es_client, restore_config)
        # 使用配置中的备份目录作为元数据根目录
        backup_dir = config.get('backup_dir', './backups')
        self.metadata_manager = MetadataManager(backup_dir)
    
    def execute(self, backup_id: str, params: Dict[str, Any]):
        """执行恢复命令
        
        Args:
            backup_id: 备份ID
            params: 恢复参数
            
        Returns:
            恢复元数据
        """
        try:
            # 验证参数
            validated_params = self.validate_parameters(params)
            
            # 检查备份是否存在
            backup_info = self._find_backup(backup_id)
            if not backup_info:
                raise ESArchiveError(f"备份不存在: {backup_id}")
            
            # 显示恢复信息
            self._show_restore_info(backup_id, backup_info, validated_params)
            
            # 确认操作
            if not self._confirm_restore(backup_info, validated_params):
                self.echo_info("恢复操作已取消")
                return None
            
            # 创建恢复策略
            strategy = RestoreStrategy(**validated_params)
            
            # 执行恢复
            self.echo_info(f"开始恢复备份: {backup_id}")
            
            with click.progressbar(
                length=100,
                label="恢复进度",
                show_percent=True,
                show_eta=True
            ) as bar:
                def progress_callback(progress_info):
                    if 'progress_percent' in progress_info:
                        bar.update(int(progress_info['progress_percent']) - bar.pos)
                
                # 执行恢复
                # 传递备份路径信息给恢复管理器
                backup_path = backup_info.get('backup_path')
                if backup_path:
                    # 临时设置备份路径到策略中
                    strategy.backup_path = backup_path

                metadata = self.restore_manager.restore_from_backup(
                    backup_id=backup_id,
                    strategy=strategy
                )
                
                # 完成进度条
                bar.update(100 - bar.pos)
            
            self.echo_success("恢复完成")
            self._show_restore_result(metadata)
            
            return metadata
            
        except KeyboardInterrupt:
            self.handle_keyboard_interrupt()
        except Exception as e:
            self.logger.exception("恢复执行失败")
            raise ESArchiveError(f"恢复失败: {e}")
    
    def validate_parameters(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """验证恢复参数
        
        Args:
            params: 原始参数
            
        Returns:
            验证后的参数
        """
        validated = params.copy()
        
        # 验证策略类型
        strategy_type = validated.get('strategy_type', 'full')
        if strategy_type not in ['full', 'selective', 'time_range']:
            raise ESArchiveError(f"无效的恢复策略: {strategy_type}")
        
        # 验证目标索引
        target_index = validated.get('target_index')
        if not target_index:
            raise ESArchiveError("必须指定目标索引")
        
        # 验证批量大小
        batch_size = validated.get('batch_size', 500)  # 降低默认值
        if not isinstance(batch_size, int) or batch_size <= 0:
            raise ESArchiveError("批量大小必须是正整数")
        if batch_size > 5000:  # 降低警告阈值
            self.echo_warning("批量大小过大，可能导致内存问题，建议使用自适应批量大小")

        # 验证并行工作线程数
        parallel_workers = validated.get('parallel_workers', 2)
        if not isinstance(parallel_workers, int) or parallel_workers <= 0:
            raise ESArchiveError("并行工作线程数必须是正整数")
        if parallel_workers > 8:
            self.echo_warning("并行工作线程数过多，可能影响ES集群性能")

        # 验证内存管理参数
        adaptive_batch_size = validated.get('adaptive_batch_size', True)
        if not isinstance(adaptive_batch_size, bool):
            validated['adaptive_batch_size'] = True

        circuit_breaker_retry = validated.get('circuit_breaker_retry', True)
        if not isinstance(circuit_breaker_retry, bool):
            validated['circuit_breaker_retry'] = True

        max_memory_usage = validated.get('max_memory_usage', '500MB')
        if not isinstance(max_memory_usage, str):
            validated['max_memory_usage'] = '500MB'

        max_retries = validated.get('max_retries', 3)
        if not isinstance(max_retries, int) or max_retries < 0:
            validated['max_retries'] = 3
        
        # 检查目标索引冲突
        target_index = validated['target_index']
        overwrite = validated.get('overwrite_existing', False)
        
        if self.validate_index_exists(target_index):
            if not overwrite:
                self.echo_warning(f"目标索引已存在: {target_index}，数据将追加到现有索引")
            else:
                self.echo_warning(f"目标索引将被覆盖: {target_index}")
        
        return validated
    
    def _show_restore_info(self, backup_id: str, backup_info: Dict[str, Any], params: Dict[str, Any]):
        """显示恢复信息
        
        Args:
            backup_id: 备份ID
            backup_info: 备份信息
            params: 恢复参数
        """
        backup_metadata = backup_info.get('full_metadata', {})
        
        click.echo("\n恢复配置:")
        click.echo(f"  备份ID: {backup_id}")
        click.echo(f"  源索引: {backup_metadata.get('source_index', '未知')}")
        click.echo(f"  目标索引: {params.get('target_index')}")
        click.echo(f"  恢复策略: {params.get('strategy_type', 'full')}")
        click.echo(f"  批量大小: {params.get('batch_size', 500)}")
        click.echo(f"  并行线程: {params.get('parallel_workers', 2)}")
        click.echo(f"  创建索引: {'是' if params.get('create_index', True) else '否'}")
        click.echo(f"  覆盖现有: {'是' if params.get('overwrite_existing', False) else '否'}")
        click.echo(f"  数据验证: {'是' if params.get('validate_data', True) else '否'}")
        click.echo(f"  自适应批量: {'是' if params.get('adaptive_batch_size', True) else '否'}")
        click.echo(f"  断路器重试: {'是' if params.get('circuit_breaker_retry', True) else '否'}")
        click.echo(f"  最大内存: {params.get('max_memory_usage', '500MB')}")
        click.echo(f"  最大重试: {params.get('max_retries', 3)}")
        
        click.echo("\n备份信息:")
        click.echo(f"  备份时间: {backup_metadata.get('start_time', '未知')}")
        click.echo(f"  文档数量: {backup_metadata.get('backed_up_documents', 0):,}")
        click.echo(f"  备份状态: {backup_metadata.get('status', '未知')}")
        
        if backup_metadata.get('compressed_size_bytes'):
            click.echo(f"  备份大小: {self.format_size(backup_metadata['compressed_size_bytes'])}")
        
        click.echo()
    
    def _confirm_restore(self, backup_info: Dict[str, Any], params: Dict[str, Any]) -> bool:
        """确认恢复操作
        
        Args:
            backup_info: 备份信息
            params: 恢复参数
            
        Returns:
            是否确认
        """
        target_index = params['target_index']
        overwrite = params.get('overwrite_existing', False)
        
        # 检查目标索引状态
        if self.validate_index_exists(target_index):
            if overwrite:
                warning_msg = f"警告: 将删除并重建索引 '{target_index}'，所有现有数据将丢失！"
                self.echo_warning(warning_msg)
                return self.confirm_action("确定要继续吗？", default=False)
            else:
                info_msg = f"目标索引 '{target_index}' 已存在，数据将追加到现有索引"
                self.echo_info(info_msg)
        
        # 显示恢复摘要
        backup_metadata = backup_info.get('full_metadata', {})
        doc_count = backup_metadata.get('backed_up_documents', 0)
        
        click.echo(f"\n即将恢复 {doc_count:,} 个文档到索引 '{target_index}'")
        
        return self.confirm_action("确定要开始恢复吗？", default=True)
    
    def _show_restore_result(self, metadata):
        """显示恢复结果
        
        Args:
            metadata: 恢复元数据
        """
        click.echo("\n恢复结果:")
        click.echo(f"  恢复ID: {metadata.restore_id}")
        click.echo(f"  目标索引: {metadata.target_index}")
        click.echo(f"  开始时间: {metadata.start_time}")
        click.echo(f"  结束时间: {metadata.end_time}")
        
        if metadata.end_time and metadata.start_time:
            duration = (metadata.end_time - metadata.start_time).total_seconds()
            click.echo(f"  持续时间: {self.format_duration(duration)}")
        
        click.echo(f"  总文档数: {metadata.total_documents:,}")
        click.echo(f"  恢复文档: {metadata.restored_documents:,}")
        click.echo(f"  跳过文档: {metadata.skipped_documents:,}")
        click.echo(f"  错误数量: {metadata.error_count}")
        click.echo(f"  状态: {metadata.status}")
        
        if metadata.error_message:
            self.echo_error(f"错误信息: {metadata.error_message}")
        
        # 计算成功率
        if metadata.total_documents > 0:
            success_rate = metadata.restored_documents / metadata.total_documents
            click.echo(f"  成功率: {success_rate:.1%}")
        
        click.echo()
    
    def preview_restore(self, backup_id: str, params: Dict[str, Any]) -> Dict[str, Any]:
        """预览恢复计划
        
        Args:
            backup_id: 备份ID
            params: 恢复参数
            
        Returns:
            恢复计划信息
        """
        try:
            # 获取备份信息
            backup_info = self.metadata_manager.get_backup_info(backup_id)
            if not backup_info:
                raise ESArchiveError(f"备份不存在: {backup_id}")
            
            backup_metadata = backup_info.get('full_metadata', {})
            strategy = RestoreStrategy(**params)
            
            return self.restore_manager.strategy_manager.create_restore_plan(
                strategy, self.es_client, backup_metadata
            )
            
        except Exception as e:
            self.logger.warning(f"预览恢复计划失败: {e}")
            return {}
    
    def show_restore_preview(self, backup_id: str, params: Dict[str, Any]):
        """显示恢复预览
        
        Args:
            backup_id: 备份ID
            params: 恢复参数
        """
        try:
            plan = self.preview_restore(backup_id, params)
            
            if not plan:
                self.echo_warning("无法生成恢复预览")
                return
            
            click.echo("\n恢复预览:")
            
            # 显示验证结果
            validation = plan.get('validation', {})
            if validation.get('warnings'):
                self.echo_warning("警告:")
                for warning in validation['warnings']:
                    click.echo(f"  - {warning}")
            
            if validation.get('errors'):
                self.echo_error("错误:")
                for error in validation['errors']:
                    click.echo(f"  - {error}")
                return
            
            # 显示时间估算
            time_estimation = plan.get('time_estimation', {})
            if time_estimation:
                click.echo(f"\n时间估算:")
                click.echo(f"  预计文档数: {time_estimation.get('estimated_documents', 0):,}")
                click.echo(f"  预计耗时: {self.format_duration(time_estimation.get('estimated_seconds', 0))}")
            
            # 显示恢复步骤
            steps = plan.get('steps', [])
            if steps:
                click.echo(f"\n恢复步骤:")
                for i, step in enumerate(steps, 1):
                    click.echo(f"  {i}. {step.get('step_name', '未知步骤')}")
                    if step.get('description'):
                        click.echo(f"     {step['description']}")
            
            click.echo()
            
        except Exception as e:
            self.echo_error(f"显示恢复预览失败: {e}")

    def _find_backup(self, backup_id: str) -> Optional[Dict[str, Any]]:
        """查找备份信息

        Args:
            backup_id: 备份ID

        Returns:
            备份信息字典，如果不存在则返回None
        """
        try:
            # 首先尝试从元数据管理器获取
            backup_info = self.metadata_manager.get_backup_info(backup_id)
            if backup_info:
                return backup_info

            # 如果没有找到，直接扫描备份目录
            backup_dir = self.config.get('backup_dir', './backups')
            backup_path = Path(backup_dir) / backup_id

            if not backup_path.exists() or not backup_path.is_dir():
                return None

            # 读取备份元数据
            metadata_file = backup_path / "metadata.json"
            if not metadata_file.exists():
                return None

            import json
            with open(metadata_file, 'r', encoding='utf-8') as f:
                metadata = json.load(f)

            # 转换为标准格式
            backup_info = {
                'backup_id': metadata.get('backup_id', backup_id),
                'backup_path': str(backup_path),
                'source_index': metadata.get('source_index', '未知'),
                'status': metadata.get('status', '未知'),
                'created_at': metadata.get('start_time', '未知'),
                'total_documents': metadata.get('total_documents', 0),
                'strategy': metadata.get('strategy', {}),
                'full_metadata': metadata
            }

            return backup_info

        except Exception as e:
            self.logger.warning(f"查找备份失败: {backup_id}, {e}")
            return None
    
    def validate_restore_integrity(self, backup_id: str, target_index: str) -> Dict[str, Any]:
        """验证恢复完整性
        
        Args:
            backup_id: 备份ID
            target_index: 目标索引
            
        Returns:
            验证结果
        """
        try:
            from ...core.validator import DataIntegrityValidator
            
            validator = DataIntegrityValidator(self.es_client)
            return validator.validate_restore_integrity(backup_id, target_index)
            
        except Exception as e:
            self.logger.error(f"验证恢复完整性失败: {e}")
            return {
                "valid": False,
                "errors": [str(e)]
            }
    
    def show_restore_validation(self, backup_id: str, target_index: str):
        """显示恢复验证结果
        
        Args:
            backup_id: 备份ID
            target_index: 目标索引
        """
        try:
            self.echo_info("验证恢复完整性...")
            
            result = self.validate_restore_integrity(backup_id, target_index)
            
            if result.get('valid'):
                self.echo_success("恢复验证通过")
            else:
                self.echo_error("恢复验证失败")
            
            # 显示详细结果
            click.echo(f"\n验证结果:")
            click.echo(f"  总检查项: {result.get('total_checks', 0)}")
            click.echo(f"  通过检查: {result.get('passed_checks', 0)}")
            click.echo(f"  失败检查: {result.get('failed_checks', 0)}")
            
            # 显示警告
            warnings = result.get('warnings', [])
            if warnings:
                self.echo_warning("警告:")
                for warning in warnings:
                    click.echo(f"  - {warning}")
            
            # 显示错误
            errors = result.get('errors', [])
            if errors:
                self.echo_error("错误:")
                for error in errors:
                    click.echo(f"  - {error}")
            
            click.echo()
            
        except Exception as e:
            self.echo_error(f"显示验证结果失败: {e}")
